package com.gitee.simpleplus.nesty.core.server;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;
import com.gitee.simpleplus.nesty.NestyConst.RequestMethod;
import com.gitee.simpleplus.nesty.commons.annotations.RestController;
import com.gitee.simpleplus.nesty.commons.annotations.RequestMapping;
import com.gitee.simpleplus.nesty.core.server.rest.ControllerRouter;
import com.gitee.simpleplus.nesty.core.server.rest.URLResource;
import com.gitee.simpleplus.nesty.core.server.rest.controller.URLController;
import com.gitee.simpleplus.nesty.core.server.rest.interceptor.Interceptor;
import com.gitee.simpleplus.nesty.proxy.ExecChainProxy;
import com.gitee.simpleplus.utils.ScanClassUtil;


public abstract class NestyServer extends NestyOptionProvider implements Server {

    // controller router map collection
    private final ControllerRouter routerTable = new ControllerRouter();
    // interceptors collection
    private Interceptor interceptor;

    // scan package controller class. NOT Threads-Safe !!
    //
    public NestyServer scanHttpController(String packageName) throws Exception {
        // scan all classes and install them
		Set<Class<?>> classes = ScanClassUtil.scan(packageName);
		interceptor = ExecChainProxy.genExecChainObj(Interceptor.class, classes);
		classes.stream().forEach(clazz -> {
			RequestMapping clazzLevelRequestMapping = null;
			// with @Controller
            if (clazz.getAnnotation(RestController.class) != null) {
                clazzLevelRequestMapping = clazz.getAnnotation(RequestMapping.class);

                // find all annotationed method in class
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (Modifier.isStatic(method.getModifiers()) || method.getAnnotations().length == 0)
                        continue;

                    // read @RequestMapping
                   RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                    if (requestMapping == null)
                        continue;

                    String uri = requestMapping.value();
                    if (!uri.startsWith("/"))
                        throw new RuntimeException(String.format("%s.%s annotation must start with / ", clazz.getName(), method.getName()));

                    if (clazzLevelRequestMapping != null)
                        uri = clazzLevelRequestMapping.value() + uri;


                    // default is RequestMethod.GET if method annotation is not set
                    RequestMethod requestMethod = requestMapping.method();

                    URLResource urlResource = URLResource.fromHttp(uri, requestMethod);
                    URLController urlController = URLController.fromProvider(uri, clazz, method);

                    /**
                     * register the controller to controller map {@link ControllerRouter}.register() will return
                     * false on dupliacted URLReousource key. Duplicated URLResource means they have same
                     * url, url variabls and http method. we will confuse on them and couldn't decide which
                     * controller method to invoke.
                     *
                     * TODO : we throw exception here. let users to know and decide what to do
                     *
                     */
                    if (!routerTable.register(urlResource, urlController))
                        throw new RuntimeException(String.format("%s.%s annotation is duplicated", clazz.getName(), method.getName()));
                }
            }
		});
        
        return this;
    }

    public ControllerRouter getRouteController() {
        return routerTable;
    }

    public Interceptor getInterceptor() {
        return interceptor;
    }

    @Override
    public void shutdown() {
    	interceptor.destroy();
    }
}